#if !defined(_VECTOR_BUFFER_H)
#define _VECTOR_BUFFER_H

#pragma once

#include <list>
using namespace std;

#include "KtCommon/ktmemallocator.h"

#define DEF_BLOCK_SIZE			8192
#ifndef Byte
#define	Byte					unsigned char
#endif // Byte

class CVectorBuffer
{
	class CDataBlock : public CObjectPool
	{
	public:
		CDataBlock(int block_size, const Byte* ptr, int len);
		virtual ~CDataBlock();

		int append(const Byte* pbData, int length);
		Byte* buffer(int& length);
		int read(Byte* buffer, int toRead);
		int erase(int length);
		int size();

	public:
		Byte*			m_datPtr;
		int				m_datPos;
		int				m_length;
		const int		m_bufsize;
	};
	typedef list<CDataBlock*>	CDataBlockList;
public:
	CVectorBuffer(const int block_size = DEF_BLOCK_SIZE);
	virtual ~CVectorBuffer();

public:
	void append(const Byte* pbData, int length);
	Byte* buffer(int& length);
	int read(Byte* buffer, int toRead);
	size_t size();
	void erase(int length);
	void clear();
	void setBlockSize(const int block_size) { m_block_size = m_block_size; }

protected:
	int				m_block_size;
	size_t			m_nDataLen;
	CDataBlockList	m_lstBlocks;
};





inline CVectorBuffer::CVectorBuffer(const int block_size)
: m_nDataLen(0)
, m_block_size(block_size)
{
}

inline CVectorBuffer::~CVectorBuffer()
{
	clear();
}

inline CVectorBuffer::CDataBlock::CDataBlock(int block_size, const Byte* ptr, int len)
: m_datPtr(NULL)
, m_datPos(0)
, m_length(0)
, m_bufsize(block_size)
{
	m_datPtr = kt_mempool_malloc(block_size);
	if (m_datPtr)
	{
		m_length = len;
		memcpy(m_datPtr, ptr, m_length);
	}
}

inline CVectorBuffer::CDataBlock::~CDataBlock()
{
	if (m_datPtr)
	{
		kt_mempool_free(m_datPtr);
		m_datPtr = NULL;
	}
	m_datPos = m_length = 0;
}

inline int CVectorBuffer::CDataBlock::append(const Byte* pbData, int length)
{
	int pos = min(m_bufsize-m_length, length);
	if (pos < 0)
		pos = 0;
	if (pos > 0)
	{
		memcpy(&m_datPtr[m_length], pbData, pos);
		m_length += pos;
	}
	return pos;
}

inline Byte* CVectorBuffer::CDataBlock::buffer(int& length)
{
	length = size();
	return &m_datPtr[m_datPos];
}

inline int CVectorBuffer::CDataBlock::read(Byte* buffer, int toRead)
{
	int to_read = min(toRead, size());
	memcpy(buffer, &m_datPtr[m_datPos], to_read);
	return to_read;
}

inline int CVectorBuffer::CDataBlock::erase(int length)
{
	int to_erase = min(length, size());
	m_datPos += to_erase;
	return to_erase;
}

inline int CVectorBuffer::CDataBlock::size()
{
	return (m_length - m_datPos); 
}

inline void CVectorBuffer::append(const Byte* pbData, int length)
{
	const int block_size = m_block_size;

	int pos = 0;
	if (!m_lstBlocks.empty())
	{
		CDataBlock* block = m_lstBlocks.back();
		pos = block->append(pbData, length);
		m_nDataLen += pos;
	}

	while (1)
	{
		const Byte* ptr = &pbData[pos];
		int block_len = min(length-pos, block_size);
		if (block_len <= 0)
			break;

		CDataBlock* block = new CDataBlock(block_size, ptr, block_len);
		if (block)
		{
			if (block->size() != block_len)
			{
				delete block;
				continue;
			}
			m_lstBlocks.push_back(block);
			pos += block_len;
			m_nDataLen += block_len;
		}
	}
}

inline Byte* CVectorBuffer::buffer(int& length)
{
	if (m_lstBlocks.empty())
	{
		length = 0;
		return NULL;
	}

	CDataBlock* block = m_lstBlocks.front();
	return block->buffer(length);
}

inline int CVectorBuffer::read(Byte* buffer, int toRead)
{
	const int block_size = m_block_size;

	int nRead = 0;
	CDataBlockList::const_iterator it;
	for (it=m_lstBlocks.begin(); it!=m_lstBlocks.end(); ++it)
	{
		int to_read = min(toRead-nRead, block_size);
		if (to_read <= 0)
			break;

		CDataBlock* block = *it;
		int nRet = block->read(&buffer[nRead], to_read);
		if (nRet > 0)
			nRead += nRet;
	}
	return nRead;
}

inline size_t CVectorBuffer::size()
{
	return m_nDataLen;
}

inline void CVectorBuffer::erase(int length)
{
	int to_erase = min(length, (int)m_nDataLen);
	while (1)
	{
		if (m_lstBlocks.empty() || to_erase <= 0)
			break;

		CDataBlock* block = m_lstBlocks.front();
		int erased = block->erase(to_erase);
		if (block->size() <= 0)
		{
			m_lstBlocks.pop_front();
			delete block;
		}
		to_erase -= erased;
		m_nDataLen -= erased;
	}
}

inline void CVectorBuffer::clear()
{
	m_nDataLen = 0;

	CDataBlockList::iterator it;
	for (it=m_lstBlocks.begin(); it!=m_lstBlocks.end(); ++it)
	{
		CDataBlock* block = *it;
		delete block;
	}
	m_lstBlocks.clear();
}

#endif //_VECTOR_BUFFER_H
